(defun doc-view-previous-page (arg)
  "Browse ARG pages backward."
  (interactive "p")
  (doc-view-next-page (* -1 arg)))

(defun doc-view-first-page ()
  "View the first page."
  (interactive)
  (doc-view-goto-page 0))

(defun doc-view-last-page ()
  "View the last page."
  (interactive)
  (doc-view-goto-page (1- (length doc-view-current-files))))

(defun doc-view-file-name-to-directory-name (file)
  "Return the directory where the png files of FILE should be saved.

It'a a subdirectory of `doc-view-cache-directory'."
  (concat (directory-file-name doc-view-cache-directory)
          "/"
          (replace-regexp-in-string "/" "!" file)))

(defun doc-view-convert-file (file)
  "Convert FILE to a set of png files, one file per page.

Those files are saved in the directory given by
`doc-view-file-name-to-directory-name'.

Returns the converter process."
  (clear-image-cache)
  (let* ((dir (doc-view-file-name-to-directory-name file))
         (png-file (concat dir "/" "page.png")))
    (when (file-exists-p dir)
      (dired-delete-file dir 'always))
    (make-directory dir t)
    (start-process "doc-view-convert" "*doc-view conversion output*"
                   doc-view-converter-program
                   "-density"  (format "%d" doc-view-display-size)
                   file
                   "-strip" "-trim"
                   png-file)))

(defun doc-view-sort (a b)
  "Return non-nil if A should be sorted before B.
Predicate for sorting `doc-view-current-files'."
  (if (< (length a) (length b))
      t
    (if (> (length a) (length b))
        nil
      (string< a b))))

(define-derived-mode doc-view-mode nil "DocView"
  "Major mode in DocView buffers.

\\{doc-view-mode-map}"
  :group 'doc-view
  (setq buffer-read-only t)
  (make-local-variable 'doc-view-current-files)
  (make-local-variable 'doc-view-current-page))

(defun doc-view-display-image (file)
  "Display the given png FILE."
  (setq buffer-read-only nil)
  (erase-buffer)
  (let ((image (create-image file 'png nil
                             :margin doc-view-display-margin)))
    (insert-image image))
  (setq buffer-read-only t))

(defun doc-view-display (dir)
  "Start viewing the document whose pages are png files in DIR."
  (let ((buffer (generate-new-buffer "*DocView*")))
    (switch-to-buffer buffer)
    (doc-view-mode)
    (setq doc-view-current-files
          (sort (directory-files dir t "page-[0-9]+\\.png" t)
                'doc-view-sort))
    (doc-view-goto-page 0)))

(defun doc-view (no-cache)
  "Query for a document, convert it to png and start viewing it.
If this file is still in the cache, don't convert and use the
existing page files.  With prefix arg NO-CACHE, don't use the
cached files and convert anew."
  (interactive "P")
  (let* ((file (expand-file-name
                (read-file-name "File: " nil nil t)))
         (dir (doc-view-file-name-to-directory-name file)))
    (setq doc-view-current-dir dir)
    (if (and (file-exists-p dir)
             (not no-cache))
        (progn
          (message "DocView: using cached files!")
          (doc-view-display dir))
      (message "DocView: starting conversion!")
      (set-process-sentinel (doc-view-convert-file file)
                            '(lambda (proc event)
                               (message "DocView: finished conversion!")
                               (doc-view-display doc-view-current-dir))))))

(defun doc-view-clear-cache ()
  "Delete the whole cache (`doc-view-cache-directory')."
  (interactive)
  (dired-delete-file doc-view-cache-directory 'always)
  (make-directory doc-view-cache-directory))

(defun doc-view-dired-cache ()
  "Open `dired' for `doc-view-cache-directory'."
  (interactive)
  (dired doc-view-cache-directory))

(provide 'doc-view)

;;; doc-view.el ends here
